
import subprocess
import time


# ================

class PikafishEngine:
    def __init__(self, engine_path):
        self.engine = subprocess.Popen(
            engine_path,
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            universal_newlines=True,
            bufsize=1
        )
        self._initialize()

    def _initialize(self):
        self.send("uci")
        self.wait_for("uciok")

    def send(self, cmd):
        self.engine.stdin.write(cmd + "\n")
        self.engine.stdin.flush()

    def wait_for(self, keyword, timeout=5):
        start_time = time.time()
        while True:
            line = self.engine.stdout.readline().strip()  # 这里容器阻塞。
            if keyword in line:
                return line
            if time.time() - start_time > timeout:
                raise TimeoutError(f"未收到响应: {keyword}")

    def get_bestmove(self, fen, depth=18):
        self.send(f"position fen {fen}")
        self.send(f"go depth {depth}")
        return self.wait_for("bestmove")

    def close(self):
        self.send("quit")
        self.engine.terminate()

# 使用示例
engine = PikafishEngine("./pikafish")
fen = "rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w - - 0 1"
bestmove = engine.get_bestmove(fen)
print("最佳走法:", bestmove)  # 输出示例: bestmove h3h4
engine.close()

# ================

# 引擎崩溃检测
if engine.poll() is not None:
    raise RuntimeError("引擎进程已终止")

"""
关键注意事项 ​​
​​ 并发控制 ​​：
每个引擎实例 ​​不能同时处理多个请求​​（UCI协议是同步的），需加锁或使用连接池。
​​引擎健康检查​​：
定期发送 isready 命令验证引擎是否存活。
​​资源监控​​：
限制最大并发引擎数，避免内存耗尽。
"""

# ==========
import subprocess
import threading
from flask import current_app


class PikafishPool:
    """线程安全的 Pikafish 引擎池"""

    def __init__(self, app=None, **kwargs):
        self.engines = []
        self.locks = []
        self.engine_path = None
        self.pool_size = 4
        if app is not None:
            self.init_app(app, **kwargs)

    def init_app(self, app, **kwargs):
        # 从 Flask 配置或 kwargs 中读取参数
        self.engine_path = kwargs.get('engine_path', app.config.get('PIKAFISH_ENGINE_PATH', './pikafish'))
        self.pool_size = kwargs.get('pool_size', app.config.get('PIKAFISH_POOL_SIZE', 4))

        # 初始化引擎池
        self._init_engines()

        # 注册优雅关闭钩子
        app.teardown_appcontext(self._close_engines)

    def _init_engines(self):
        """初始化引擎池"""
        for _ in range(self.pool_size):
            engine = subprocess.Popen(
                self.engine_path,
                stdin=subprocess.PIPE,
                stdout=subprocess.PIPE,
                universal_newlines=True,
                bufsize=1
            )
            engine.stdin.write("uci\n")
            engine.stdin.flush()
            self.engines.append(engine)
            self.locks.append(threading.Lock())

    def get_engine(self):
        """获取一个可用引擎（线程安全）"""
        for i, lock in enumerate(self.locks):
            if lock.acquire(blocking=False):
                try:
                    # 检查引擎是否存活
                    if self.engines[i].poll() is not None:
                        self._restart_engine(i)
                    return self.engines[i]
                except:
                    lock.release()
                    raise
        raise RuntimeError("No available engines in pool")

    def release_engine(self, engine):
        """释放引擎回池"""
        for i, e in enumerate(self.engines):
            if e == engine:
                self.locks[i].release()
                return
        raise ValueError("Engine not from this pool")

    def _restart_engine(self, index):
        """重启崩溃的引擎"""
        self.engines[index].terminate()
        new_engine = subprocess.Popen(
            self.engine_path,
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            universal_newlines=True,
            bufsize=1
        )
        new_engine.stdin.write("uci\n")
        new_engine.stdin.flush()
        self.engines[index] = new_engine

    def _close_engines(self, exception=None):
        """关闭所有引擎"""
        for engine in self.engines:
            if engine.poll() is None:
                engine.stdin.write("quit\n")
                engine.stdin.flush()
                engine.terminate()


# 创建扩展实例
pikafish_pool = PikafishPool()

# 释放资源。


# 带释放引擎的代码。

from flask import Flask
import atexit

class PikafishExtension:
    def __init__(self, app=None):
        self.app = app
        self.pool = None
        if app is not None:
            self.init_app(app)

    def init_app(self, app):
        self.pool = PikafishPool()  # 初始化引擎池
        app.teardown_appcontext(self._teardown)
        atexit.register(self._cleanup)

    def _teardown(self, exception=None):
        if self.pool:
            self.pool.close_all_engines()

    def _cleanup(self):
        self._teardown()

# 使用扩展
app = Flask(__name__)
pikafish_ext = PikafishExtension(app)



